﻿using MiniFox.Data.Mapping;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace MiniFox.Data.NativeClient
{
    /// <summary>
    /// 实体查询对象(可用于配置化)
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    public class NativeObjectQuery<TEntity> : NativeDataCommand<TEntity>, IObjectQuery<TEntity> where TEntity : class
    {
        /// <summary>
        /// 
        /// </summary>
        string _baseSql;
        /// <summary>
        /// 
        /// </summary>
        ParameterCollection _parameters;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="database"></param>
        public NativeObjectQuery(Database database) : base(database)
        {
        }
        /// <summary>
        /// 
        /// </summary>
        protected virtual string BaseSqlText
        {
            get
            {
                if (string.IsNullOrWhiteSpace(_baseSql))
                {
                    _baseSql = DatabaseUtil.ResolveBaseSqlExpression<TEntity>(this.DbSchema);
                }
                return this._baseSql;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        protected virtual ParameterCollection Parameters
        {
            get
            {
                if (_parameters == null)
                    _parameters = new ParameterCollection();
                return _parameters;
            }
        }
        ///// <summary>
        ///// 
        ///// </summary>
        ///// <param name="exp"></param>
        ///// <param name="pExp"></param>
        ///// <returns></returns>
        //protected virtual string ResolveExpression(Expression exp, ParameterExpression pExp)
        //{
        //    if (typeof(TEntity) != pExp.Type)
        //        throw new InvalidOperationException();
        //    string sql = DatabaseUtil.ResolveConditionExpression(exp, pExp, this.Parameters, this.Database.ProviderName);

        //    return sql;
        //}

        /// <summary>
        /// 
        /// </summary>
        /// <param name="lambda"></param>
        /// <returns></returns>
        protected virtual string ResolveExpression(Expression<Func<TEntity, bool>> lambda)
        {
            this.Parameters.Clear();
            string mainSql = this.BaseSqlText;
            if (lambda != null)
            {
                Expression exp = lambda.Body;
                ParameterExpression pExp = lambda.Parameters[0];
                if (typeof(TEntity) != pExp.Type)
                    throw new InvalidOperationException();
                string condition = DatabaseUtil.ResolveConditionExpression(exp, pExp, this.Parameters, this.Database.ParameterPrefix);

                mainSql += (" WHERE " + condition);
            }
            return mainSql;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="lambda"></param>
        /// <returns></returns>
        public virtual TEntity Execute(Expression<Func<TEntity, bool>> lambda)
        {
            string sqlstr = ResolveExpression(lambda);
            TEntity entity = null;
            Dictionary<string, object> paramDict = new Dictionary<string, object>();
            try
            {
                List<DbParameter> parameterlist = new List<DbParameter>();
                foreach (var p in this.Parameters)
                {
                    DbParameter par = this.Database.CreateParameter(p.ParameterName, p.Name, p.ClrType, p.Direction);
                    par.Value = p.DefaultValue;
                    parameterlist.Add(par);
                    paramDict.Add(par.ParameterName, par.Value);
                }
                this.OnCreated(sqlstr, paramDict);
                entity = Database.LoadObject<TEntity>(sqlstr, CommandType.Text, parameterlist.ToArray());

                this.OnExecuted(sqlstr, paramDict);
            }
            catch (Exception e)
            {
                this.OnErrorHandled(sqlstr, paramDict, e);
            }
            return entity;
        }

    }

}
